#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
安全生产日报 关键信息统一提取器

将各表的关键信息抽取脚本整合为一个入口：info_extract.py

支持的类型：
1. 安全系统违章清单
2. 安委办检查组检查情况（自动处理“每日安全检查记录”和“巡屏小组检查日报”）
3. 设备维修重点关注
4. 生产作业重点关注
5. 重大工程风险提示
"""
import os
import json
from abc import abstractmethod
from datetime import datetime
from typing import Any, Dict, List, Optional

from loguru import logger


class BaseExtractor:
    def __init__(self):
        pass

    def ensure_parent_dir(self, output_file_path: str) -> None:
        parent_dir = os.path.dirname(output_file_path)
        if parent_dir:
            os.makedirs(parent_dir, exist_ok=True)

    # 读取 JSON 文件并返回字典；若失败返回 None。
    def read_json_file(self, file_path: str) -> Optional[Dict[str, Any]]:
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            logger.error(f'读取JSON失败: {file_path} -> {e}')
            return None

    # 将字典数据保存为 JSON 文件；成功返回 True，否则 False。
    def save_json_file(self, data: Dict[str, Any], output_file_path: str) -> bool:
        try:
            self.ensure_parent_dir(output_file_path)
            with open(output_file_path, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
            return True
        except Exception as e:
            logger.error(f'保存JSON失败: {output_file_path} -> {e}')
            return False

    # 将任意值安全转换为去除首尾空白的字符串；None -> ''。
    def to_string_or_empty(self, value: Any) -> str:
        if value is None:
            return ''
        return str(value).strip()

    # 判断 text 的字符串表示中是否包含任一子串（大小写不敏感）。
    def match_any(self, substrs: List[str], text: Any) -> bool:
        s = str(text).lower()
        return any(sub.lower() in s for sub in substrs)

    @abstractmethod
    def extract(self, json_file_path):
        pass


# ==================== 类型 1：安全系统违章清单 ====================
class ViolationFileExtractor(BaseExtractor):
    def extract(self, json_file_path: str):
        """
        从“安全系统违章清单.json”中抽取关键信息（字段子集），返回统一结构的字典。
        输出结构与原独立脚本保持一致，便于下游使用。
        """
        print(f'正在提取违章清单关键信息: {json_file_path}')

        src = self.read_json_file(json_file_path)
        if src is None:
            return None

        target_fields = [
            '上报单位',
            '上报科室',
            '违章等级',
            '违章事实描述',
            '处罚信息_人员类型',
            '处罚信息_责任人',
            '处罚信息_责任人处罚情况'
        ]

        extracted_records: List[Dict[str, str]] = []

        for index, record in enumerate(src.get('data', [])):
            slim: Dict[str, str] = {}
            for field in target_fields:
                slim[field] = self.to_string_or_empty(record.get(field, ''))
            slim['序号'] = str(index + 1)
            extracted_records.append(slim)

        result = {
            'metadata': {
                'title': '安全系统违章清单关键信息',
                'source_file': src.get('metadata', {}).get('source_file', ''),
                'total_records': len(extracted_records),
                'extracted_fields': target_fields
            },
            'data': extracted_records
        }
        return result


# ==================== 类型 2：安委办检查组检查情况 ====================
class SafetyCheckFileExtractor(BaseExtractor):
    def extract_daily_check_info(self, json_file_path: str, target_date_patterns: Optional[List[str]] = None) -> List[Dict[str, str]]:
        """
            从“每日安全检查记录.json”中抽取目标日期的字段子集。
            target_date_patterns 支持多种日期格式匹配，例如 ['2025-08-11', '8月11', '8-11']。
            """
        if target_date_patterns is None:
            target_date_patterns = ['2025-08-11', '8月11', '8-11', '8.11']

        src = self.read_json_file(json_file_path)
        if src is None:
            return []

        records = src.get('data', [])
        extracted: List[Dict[str, str]] = []

        for record in records:
            date_value = self.to_string_or_empty(record.get('日期', ''))
            if self.match_any(target_date_patterns, date_value):
                extracted.append({
                    '日期': date_value,
                    '存在问题': self.to_string_or_empty(record.get('存在问题', '')),
                    '检查人': self.to_string_or_empty(record.get('检查组成员', ''))
                })
        return extracted

    def extract_patrol_report_info(self, json_file: str, target_date_patterns: Optional[List[str]] = None) -> List[Dict[str, str]]:
        """
        从“巡屏小组检查日报.json”中抽取目标日期的字段子集。
        """
        if target_date_patterns is None:
            target_date_patterns = ['8.11', '8月11', '8-11', '2025-08-11']

        src = self.read_json_file(json_file)
        if src is None:
            return []

        records = src.get('data', [])
        extracted: List[Dict[str, str]] = []

        for record in records:
            date_value = self.to_string_or_empty(record.get('日期', ''))
            if self.match_any(target_date_patterns, date_value):
                extracted.append({
                    '日期': date_value,
                    '检查内容': self.to_string_or_empty(record.get('检查内容', '')),
                    '安全隐患': self.to_string_or_empty(record.get('安全隐患', '')),
                    '巡屏检查人': self.to_string_or_empty(record.get('巡屏检查人', ''))
                })
        return extracted

    def extract_safety_office_info(self,
                                   daily_check_file: str,
                                   patrol_report_file: str,
                                   target_date_label: str = '8月11日') -> Optional[Dict[str, Any]]:
        """
        同时处理“每日安全检查记录.json”和“巡屏小组检查日报.json”，返回合并后的抽取结果。
        输出结构与原独立脚本保持一致。
        """
        print('正在提取安委办检查组检查情况信息...')

        daily_info = self.extract_daily_check_info(daily_check_file)
        patrol_info = self.extract_patrol_report_info(patrol_report_file)

        result = {
            'metadata': {
                'target_date': target_date_label,
            },
            'extracted_info': {
                '每日安全检查记录': daily_info,
                '巡屏小组检查日报': patrol_info
            }
        }
        return result


# ==================== 类型 3：设备维修重点关注 ====================
class EquipmentMaintenanceExtractor(BaseExtractor):
    def extractor(self, json_file_path: str):
        """
            从“设备外委施工项目风险管控台账_8月11日.json”中抽取字段子集。
            输出结构与原独立脚本保持一致。
            """
        print('正在提取设备维修重点关注信息...')

        src = self.read_json_file(json_file_path)
        if src is None:
            return None

        records = src.get('data', [])
        extracted: List[Dict[str, str]] = []

        for record in records:
            extracted.append({
                '项目单位': self.to_string_or_empty(record.get('项目单位', '')),
                '项目名称': self.to_string_or_empty(record.get('项目名称', '')),
                '作业地点': self.to_string_or_empty(record.get('作业地点', '')),
                '项目存在的风险': self.to_string_or_empty(record.get('项目存在的风险', '')),
                '管控措施': self.to_string_or_empty(record.get('管控措施', '')),
            })

        result = {
            'metadata': {
                'source_file': '设备外委施工项目风险管控台账_8月11日.json',
                'total_records': len(extracted),
                'extracted_fields': ['项目单位', '项目名称', '作业地点', '项目存在的风险', '管控措施']
            },
            'extracted_info': {
                '设备外委施工项目风险管控台账': extracted
            }
        }
        return result


# ==================== 类型 4：生产作业重点关注 ====================
class NightSafetyCheckExtractor(BaseExtractor):
    def extractor(self, parsed_data_file: str, target_units: Optional[List[str]] = None):
        """
            从“各单位夜间安全检查重点_8月11日.json”中抽取一公司/二公司等目标单位的字段子集。
            输出结构与原独立脚本保持一致。
            """
        if target_units is None:
            target_units = ['一公司', '二公司']

        print('正在提取生产作业重点关注信息...')

        src = self.read_json_file(parsed_data_file)
        if src is None:
            return None

        records = src.get('data', [])
        fields = [
            '单位', '泊位', '船舶作业进度', '人员风险', '货物风险',
            '环境风险', '其他风险', '重点管控措施', '库区位置', '库场风险', '控制措施'
        ]

        extracted_records: List[Dict[str, str]] = []

        for record in records:
            unit = self.to_string_or_empty(record.get('单位', ''))
            if unit in target_units:
                slim: Dict[str, str] = {}
                for field in fields:
                    slim[field] = self.to_string_or_empty(record.get(field, ''))
                extracted_records.append(slim)

        # 构建输出
        result = {
            'metadata': {
                'extract_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                'source_file': '各单位夜间安全检查重点_8月11日.json',
                'target_units': target_units,
                'extracted_fields': fields,
                'total_records': len(extracted_records),
                'description': '生产作业重点关注信息抽取结果'
            },
            'extracted_info': {
                '生产作业重点关注': extracted_records
            }
        }
        return result


# ==================== 类型 5：重大工程风险提示 ====================
class RiskWarningExtractor(BaseExtractor):
    def extract(self, json_file_path):
        """
            从“工程外委施工项目风险管控台账_8月11日.json”中抽取字段子集。
            输出结构与原独立脚本保持一致。
            """
        print('正在提取重大工程风险提示信息...')

        src = self.read_json_file(json_file_path)
        if src is None:
            return None

        records = src.get('data', [])
        fields = ['项目单位', '项目名称', '作业地点', '项目存在的风险', '管控措施']

        extracted_records: List[Dict[str, str]] = []
        for record in records:
            slim: Dict[str, str] = {}
            for field in fields:
                slim[field] = self.to_string_or_empty(record.get(field, ''))
            extracted_records.append(slim)

        result = {
            'metadata': {
                'extract_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                'source_file': '工程外委施工项目风险管控台账_8月11日.json',
                'extracted_fields': fields,
                'total_records': len(extracted_records),
                'description': '重大工程风险提示信息抽取结果'
            },
            'extracted_info': {
                '重大工程风险提示': extracted_records
            }
        }
        return result


# ==================== 类型选择与批处理入口 ====================
def extract_by_type(file_type: str, inputs: Dict[str, str], output_file: str) -> bool:
    """
    根据 file_type 选择对应的提取策略。

    参数：
    - file_type: 类型名称（见 TYPE_CONFIGS 的键）
    - inputs:    输入路径字典（不同类型需要的键不同）
    - output_file: 输出 JSON 文件路径
    """
    try:
        # 创建对应的提取器实例
        if file_type == '安全系统违章清单':
            extractor = ViolationFileExtractor()
            result = extractor.extract(inputs['main'])
        elif file_type == '安委办检查组检查情况':
            extractor = SafetyCheckFileExtractor()
            result = extractor.extract_safety_office_info(
                inputs['daily'], inputs['patrol'])
        elif file_type == '设备维修重点关注':
            extractor = EquipmentMaintenanceExtractor()
            result = extractor.extractor(inputs['main'])
        elif file_type == '生产作业重点关注':
            extractor = NightSafetyCheckExtractor()
            result = extractor.extractor(inputs['main'])
        elif file_type == '重大工程风险提示':
            extractor = RiskWarningExtractor()
            result = extractor.extract(inputs['main'])
        else:
            print(f'不支持的类型: {file_type}')
            return False

        if result is None:
            print('  - 提取结果为空或失败')
            return False

        # 使用基类的保存方法
        if BaseExtractor().save_json_file(result, output_file):
            print(f'  - ✓ 提取成功，已保存: {output_file}')
            return True
        else:
            print('  - ✗ 保存失败')
            return False
    except KeyError as e:
        print(f'  - ✗ 输入路径配置缺失键: {e}')
        return False
    except Exception as e:
        print(f'  - ✗ 提取过程异常: {e}')
        return False


if __name__ == '__main__':
    BASE = '/root/workspace/Task3_水科院/todo_4_安全生产日报'

    TYPE_CONFIGS: Dict[str, Dict[str, Any]] = {
        # 类型：安全系统违章清单
        '安全系统违章清单': {
            'inputs': {
                'main': f'{BASE}/table1_各单位违章查出情况/parse_results/安全系统违章清单.json',
            },
            'output': f'{BASE}/table1_各单位违章查出情况/extracted_info/违章清单关键信息.json'
        },
        # 类型：安委办检查组检查情况（同时依赖两个输入文件）
        '安委办检查组检查情况': {
            'inputs': {
                'daily': f'{BASE}/table2_安委办检查组检查情况/parse_results/每日安全检查记录.json',
                'patrol': f'{BASE}/table2_安委办检查组检查情况/parse_results/巡屏小组检查日报.json',
            },
            'output': f'{BASE}/table2_安委办检查组检查情况/extracted_info/8月11日检查情况提取结果.json'
        },
        # 类型：设备维修重点关注
        '设备维修重点关注': {
            'inputs': {
                'main': f'{BASE}/table3_设备维修重点关注/parse_results/设备外委施工项目风险管控台账_8月11日.json',
            },
            'output': f'{BASE}/table3_设备维修重点关注/extracted_info/设备维修重点关注提取结果.json'
        },
        # 类型：生产作业重点关注
        '生产作业重点关注': {
            'inputs': {
                'main': f'{BASE}/table4_生产作业重点关注/parse_results/各单位夜间安全检查重点_8月11日.json',
            },
            'output': f'{BASE}/table4_生产作业重点关注/extracted_info/生产作业重点关注抽取结果.json'
        },
        # 类型：重大工程风险提示
        '重大工程风险提示': {
            'inputs': {
                'main': f'{BASE}/table5_重大工程风险提示/parse_results/工程外委施工项目风险管控台账_8月11日.json',
            },
            'output': f'{BASE}/table5_重大工程风险提示/extracted_info/重大工程风险提示抽取结果.json'
        },
    }

    for file_type, cfg in TYPE_CONFIGS.items():
        print(f'处理类型: {file_type}')
        inputs: Dict[str, str] = cfg['inputs']
        output_file: str = cfg['output']

        # 输入文件存在性检查（全部需要存在）
        missing_inputs = [p for p in inputs.values() if not os.path.exists(p)]
        if missing_inputs:
            print('  - ✗ 缺少以下输入文件:')
            for p in missing_inputs:
                print(f'      {p}')
            print()
            continue

        success = extract_by_type(file_type, inputs, output_file)
        if success:
            logger.success(f'✓ 解析成功')
            print()
        else:
            logger.error(f'✗ 解析失败')
            print()
